package com.xgk.boot.module.core.service.price;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.xgk.boot.framework.common.constant.ErrorCodeConstants;
import com.xgk.boot.framework.common.enums.CommonStatusEnum;
import com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil;
import com.xgk.boot.framework.common.pojo.PageResult;
import com.xgk.boot.framework.common.util.json.JsonUtils;
import com.xgk.boot.framework.common.util.object.BeanUtils;
import com.xgk.boot.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.xgk.boot.module.core.controller.admin.price.vo.LgExtrasDictReqVO;
import com.xgk.boot.module.core.controller.admin.price.vo.PriceRuleReqVO;
import com.xgk.boot.module.core.controller.admin.price.vo.PriceRuleSaveVO;
import com.xgk.boot.module.core.dal.dto.SegmentPriceRuleDto;
import com.xgk.boot.module.core.dal.entity.price.LgExtrasDictDO;
import com.xgk.boot.module.core.dal.entity.price.PriceRuleDO;
import com.xgk.boot.module.core.dal.iservice.PriceRuleService;
import com.xgk.boot.module.core.dal.mapper.price.PriceRuleMapper;
import com.xgk.boot.module.core.dal.redis.RedisKeyConstants;
import com.xgk.boot.module.core.enums.biz.CalcTypeEnum;
import com.xgk.boot.module.core.enums.biz.PriceRuleTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;


/**
 * 价格规则设置操作类：
 *
 * 注意：启动的价格规则不允许修改和删除
 */
@Slf4j
@Service
public class PriceRuleServiceImpl extends ServiceImpl<PriceRuleMapper, PriceRuleDO> implements PriceRuleService {

    @Autowired
    private CacheManager cacheManager;

    public boolean savePriceRule(PriceRuleSaveVO priceRule) {
        PriceRuleDO one = this.getOne(new LambdaQueryWrapperX<PriceRuleDO>().eq(PriceRuleDO::getRuleCode, priceRule.getRuleCode()));
        if(one != null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_REPEAT,"code");
        }
        preParamerCheck(priceRule);
        PriceRuleDO priceRuleDO =new PriceRuleDO();
        BeanUtils.copyProperties(priceRule,priceRuleDO);
        priceRuleDO.setStatus(CommonStatusEnum.DISABLE.getStatus());
        return this.save(priceRuleDO);
    }

    private void preParamerCheck(PriceRuleSaveVO priceRule){
        if(!PriceRuleTypeEnum.Basic_Rate.getType().equals(priceRule.getRuleType())
          && !PriceRuleTypeEnum.Extras_Fee.getType().equals(priceRule.getRuleType())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_PARAMER_EXCEPTION,"rule type");
        }
        boolean ifCalcType =false;
        for (CalcTypeEnum value : CalcTypeEnum.values()) {
            if(value.getType().equals(priceRule.getCalculationRule())){
                ifCalcType =true;
                break;
            }
        }
        if(!ifCalcType){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_PARAMER_EXCEPTION,"calculation Rule");
        }

        if(priceRule.getRuleType().equals(PriceRuleTypeEnum.Basic_Rate.getType())){
            priceRule.setFeeItemCode(PriceRuleTypeEnum.Basic_Rate.name());
        }

        if(priceRule.getCalculationRule().equals(CalcTypeEnum.Segmented.getType())){
            String ruleExtendValue = priceRule.getRuleExtendValue();
            List<SegmentPriceRuleDto> segmentPriceRuleDtos = Lists.newArrayList();
            try {
                segmentPriceRuleDtos = JsonUtils.parseArray(ruleExtendValue, SegmentPriceRuleDto.class);
            }catch (Throwable t){
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_PARAMER_EXCEPTION,"rule extend value");
            }
            if(!isCompleteCoverage(segmentPriceRuleDtos)){
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_PARAMER_INCORRECT,"extend rule value config");
            }
        }else {
            if(priceRule.getRuleValue().doubleValue() <= 0){
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_PARAMER_INCORRECT,"rule value");
            }
        }
    }

    /**
     * 校验区间是否完全覆盖所有 >=0 的值且无重叠
     * @param rules 分段规则列表（无需预先排序）
     * @return true=完全覆盖且无重叠，false=存在漏洞或重叠
     */
    public boolean isCompleteCoverage(List<SegmentPriceRuleDto> rules) {
        // 1. 检查列表非空
        if (rules == null || rules.isEmpty()) {
            return false;
        }

        // 2. 按 rangeBegin 升序排序（处理可能的 null）
        rules.sort(Comparator.comparing(
                SegmentPriceRuleDto::getRangeBegin,
                Comparator.nullsFirst(BigDecimal::compareTo)
        ));

        // 3. 检查第一个区间必须从 0 开始
        SegmentPriceRuleDto firstRule = rules.get(0);
        if (firstRule.getRangeBegin() == null ||
                firstRule.getRangeBegin().compareTo(BigDecimal.ZERO) != 0) {
            return false;
        }
        // 4. 遍历检查所有区间
        BigDecimal previousEnd = BigDecimal.ZERO;
        for (SegmentPriceRuleDto current : rules) {
            BigDecimal begin = current.getRangeBegin();
            BigDecimal end = current.getRangeEnd();

            // 4.1 检查当前区间合法性
            if (begin == null || begin.compareTo(previousEnd) != 0) {
                return false; // 必须紧接上一个区间的 end
            }
            if (end != null && end.compareTo(begin) <= 0) {
                return false; // rangeEnd 必须 > rangeBegin
            }

            previousEnd = end; // 更新上一个区间的 end
        }
        // 5. 最后一个区间可以是无限大（rangeEnd=null），否则未完全覆盖
        return previousEnd == null;
    }

    /**
     * 更新价格规则
     */
    @CacheEvict(value = RedisKeyConstants.PRICE_RULE,key="#priceRule.id")
    public boolean updatePriceRule(PriceRuleSaveVO priceRule) {
        PriceRuleDO byId = this.getById(priceRule.getId());
        if(byId == null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EXISTS,"price rule");
        }
        preParamerCheck(priceRule);
//        if(CommonStatusEnum.isEnable(byId.getStatus())){
//            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_NO_CHANGE,"enable rule");
//        }
        if(!priceRule.getRuleCode().equals(byId.getRuleCode())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_NO_CHANGE,"code");
        }
        BeanUtils.copyProperties(priceRule, byId);
        boolean updateStatus = this.updateById(byId);
        cacheEvictByCode(byId.getRuleCode());
        return updateStatus;
    }

    /**
     * 更新定价规则状态
     * @param id
     * @param priceRule
     * @return
     */
    @CacheEvict(value = RedisKeyConstants.PRICE_RULE,key="#id")
    public boolean updatePriceRuleStatus(Long id,PriceRuleSaveVO priceRule) {
        PriceRuleDO byId = this.getById(id);
        if (byId == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EXISTS, "price rule");
        }
        if(!PriceRuleTypeEnum.Basic_Rate.getType().equals(priceRule.getStatus())
                && !PriceRuleTypeEnum.Extras_Fee.getType().equals(priceRule.getStatus())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_PARAMER_EXCEPTION,"rule type");
        }
        byId.setStatus(priceRule.getStatus());
        boolean updateStatus =  this.updateById(byId);
        cacheEvictByCode(byId.getRuleCode());
        return updateStatus;
    }

    /**
     * 删除价格规则
     */
    @CacheEvict(value = RedisKeyConstants.PRICE_RULE,key="#id")
    public boolean deletePriceRule(Long id) {
        PriceRuleDO byId = this.getById(id);
        if(byId == null){
           return true;
        }
        if(CommonStatusEnum.isEnable(byId.getStatus())){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_NO_DELETE,"Enable rule");
        }
        boolean status = this.removeById(id);
        cacheEvictByCode(byId.getRuleCode());
        return status;
    }

    /**
     * 根据ID查询价格规则
     */
    @Cacheable(value = RedisKeyConstants.PRICE_RULE,key="#id", unless = "#result == null")
    public PriceRuleDO getPriceRuleById(Long id) {
        return this.getById(id);
    }

    @Cacheable(value = RedisKeyConstants.PRICE_RULE_CODE,key="#ruleCode", unless = "#result == null")
    public PriceRuleDO getPriceRuleByCode(String ruleCode){
        if(StrUtil.isBlank(ruleCode)){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EMPTY,"rule code");
        }
        LambdaQueryWrapperX<PriceRuleDO> eq = new LambdaQueryWrapperX<PriceRuleDO>().eq(PriceRuleDO::getRuleCode, ruleCode);
        return this.getOne(eq);
    }

    public List<PriceRuleDO> getPriceRuleByIds(Collection<Long> ids){
        if(ids ==null || ids.isEmpty()){
            return Lists.newArrayList();
        }
        return this.listByIds(ids);
    }

    /**
     * 查询所有价格规则
     */
    public List<PriceRuleDO> listAllPriceRules() {
        return this.list();
    }


    public PageResult<PriceRuleDO> pageList(PriceRuleReqVO priceRuleReqVO) {
        LambdaQueryWrapperX<PriceRuleDO> priceRuleWrapper = new LambdaQueryWrapperX<>();
        priceRuleWrapper.eqIfPresent(PriceRuleDO::getCalculationRule, priceRuleReqVO.getCalculationRule());
        priceRuleWrapper.eqIfPresent(PriceRuleDO::getRuleType, priceRuleReqVO.getRuleType());
        priceRuleWrapper.eqIfPresent(PriceRuleDO::getStatus, priceRuleReqVO.getStatus());

        priceRuleWrapper.likeIfPresent(PriceRuleDO::getRuleName, priceRuleReqVO.getRuleName());
        priceRuleWrapper.likeIfPresent(PriceRuleDO::getRuleCode, priceRuleReqVO.getRuleCode());
        priceRuleWrapper.likeIfPresent(PriceRuleDO::getFeeItemCode, priceRuleReqVO.getFeeItemCode());

        Page<PriceRuleDO> page = new Page<>(priceRuleReqVO.getCurrent(), priceRuleReqVO.getPageSize());
        Page<PriceRuleDO> pageRes = this.page(page, priceRuleWrapper);
        return new PageResult<>(pageRes.getRecords(), pageRes.getTotal(), priceRuleReqVO.getCurrent(), priceRuleReqVO.getPageSize());
    }

    public List<PriceRuleDO> listPriceRules(String type ,PriceRuleReqVO priceRuleReqVO) {
        LambdaQueryWrapperX<PriceRuleDO> priceRuleWrapper = new LambdaQueryWrapperX<>();
        if(type.equals(PriceRuleTypeEnum.Basic_Rate.name())){
            priceRuleWrapper.eq(PriceRuleDO::getRuleType, PriceRuleTypeEnum.Basic_Rate.getType());

        }else if(type.equals(PriceRuleTypeEnum.Extras_Fee.name())){
            priceRuleWrapper.eq(PriceRuleDO::getRuleType, PriceRuleTypeEnum.Extras_Fee.getType());
        }else {
            return Lists.newArrayList();
        }
        priceRuleWrapper.eqIfPresent(PriceRuleDO::getCalculationRule, priceRuleReqVO.getCalculationRule());
        if(priceRuleReqVO.getStatus() == null){
            priceRuleWrapper.eq(PriceRuleDO::getStatus, CommonStatusEnum.ENABLE.getStatus());
        }else {
            priceRuleWrapper.eqIfPresent(PriceRuleDO::getStatus, priceRuleReqVO.getStatus());
        }
        priceRuleWrapper.likeIfPresent(PriceRuleDO::getRuleName, priceRuleReqVO.getRuleName());
        priceRuleWrapper.likeIfPresent(PriceRuleDO::getRuleCode, priceRuleReqVO.getRuleCode());
        priceRuleWrapper.likeIfPresent(PriceRuleDO::getFeeItemCode, priceRuleReqVO.getFeeItemCode());

        List<PriceRuleDO> listRes = this.list(priceRuleWrapper);
        return listRes;
    }

    private void cacheEvictByCode(String code){
        cacheManager.getCache(RedisKeyConstants.PRICE_RULE_CODE).evict(code);
    }
}
